Modelar una partícula:
Yo estaba esperando
In [1]:
x = 1
v = 2
m = 0.5
Out[1]:
In [2]:
dt = 0.125
x += v*dt
Out[2]:
In [3]:
x2 = 2
v2 = 10
Out[3]:
In [5]:
N = 10 # numero de particulas
Out[5]:
In [7]:
x = zeros(N)
v = ones(N);
Muchos hicieron
In [8]:
particula = [1, 2, 0.5]
Out[8]:
La posición es
In [9]:
particula[1]
Out[9]:
Para actualizar su posición:
In [11]:
particula[1] += dt*particula[2]
# equivalente a particula[1] = particula[1] + dt*particula[2]
Out[11]:
¡Es illegible! Queremos nombres que reflejen la función de cada variable: x
y v
, o pos
y vel
, o posicion
y velocidad
Es la solución: combinación de variables con nombres, adentro de una sola estructura.
In [12]:
type Particula
x
v
m
end
Aquí estamos definiendo un nuevo tipo de datos, que se llama Particula
, y especificamos las características / propiedades internas de lo que es una Particula
-- la estructura de una Particula
.
Define una plantilla de una cajita que contiene espacio para estas tres variables (x
, v
y m
).
¡Todavía no hay ninguna partícula! Para crear un objeto de este tipo (Particula
): Utilizo Particula
como si fuera una función:
In [13]:
p = Particula(1., 2., 0.5)
Out[13]:
In [14]:
p
Out[14]:
In [15]:
p.x
Out[15]:
In [16]:
p.v
Out[16]:
In [17]:
p.m
Out[17]:
In [18]:
typeof(p)
Out[18]:
Quiero definir una función mover
que acepte un objeto de tipo Particula
:
In [19]:
function mover(p, dt)
p.x + p.v*dt
end
Out[19]:
In [20]:
p
Out[20]:
In [21]:
mover(p, 0.1)
Out[21]:
In [22]:
p
Out[22]:
¡No se movió! Sólo se calculó la posición nueva. Para mover realmente el objeto, tenemos que cambiar sus propiedades internas:
In [24]:
function mover!(p, dt)
# ! es una convención en Julia que dice que la función
# *modifica* su argumento -- tiene un *efecto secundario* (side effect)
p.x += p.v*dt
end
Out[24]:
In [25]:
mover!(p, 1)
Out[25]:
In [26]:
p
Out[26]:
No llamé a la función mover_particula
In [27]:
mover!(1, 0.1)
In [33]:
workspace() # borra todo lo que está definido
In [36]:
type Prueba
x
v
David
end
In [32]:
p = Particula(1,2,3) # borré la definición
In [34]:
function mover!(p, dt)
# ! es una convención en Julia que dice que la función
# *modifica* su argumento -- tiene un *efecto secundario* (side effect)
p.x += p.v*dt
end
Out[34]:
In [37]:
d = Prueba(1, 2, 3)
Out[37]:
In [38]:
mover!(d, 0.1)
Out[38]:
In [39]:
d
Out[39]:
Mi función como tal mueve cualquier cosa que tenga un x
y v
adentro. Si no quiero esto:
In [41]:
type Particula
x::Float64 # sólo puede ser de tipo Float64
v
m
end
In [43]:
function mover!(p::Particula, dt)
# sólo actúa sobre objetos de tipo Particula
p.x += p.v*dt
end
Out[43]:
In [44]:
p = Particula(1, 2, 0.5)
Out[44]:
In [46]:
mover!(p, 0.1)
Out[46]:
In [47]:
p
Out[47]:
In [48]:
methods(mover!)
Out[48]:
In [49]:
mover!(p, dt) = println("¡HOLA!")
Out[49]:
In [50]:
p
Out[50]:
In [51]:
mover!(p, 0.1)
Out[51]:
In [52]:
p
Out[52]:
In [53]:
mover!(d, 0.1)
In [54]:
mover!(i::Int) = println(i)
Out[54]:
In [55]:
methods(mover!)
Out[55]:
In [56]:
mover!(1)
Un gas consiste en muchas partículas.
In [58]:
workspace()
In [60]:
type Particula
x
v
m
end
In [59]:
type Gas_Malo
x::Vector{Float64}
v::Vector{Float64}
m::Vector{Float64}
end
Esto es una mala idea, ya que no vemos que un Gas
consiste en un montón de Partículas
.
In [61]:
type Gas
N::Int
particulas::Vector{Particula}
end
Julia automáticamente (por defecto) crea una función constructora (o un constructor): una función con el mismo nombre que el tipo, que toma los datos y crea un objeto nuevo de este tipo, con los datos adentro.
In [66]:
g = Gas(2, [Particula(1,2,3), Particula(4, 5, 6)] )
Out[66]:
In [67]:
g.N
Out[67]:
In [68]:
g.particulas
Out[68]:
In [69]:
g2 = Gas(5, [Particula(1,2,3), Particula(4, 5, 6)] )
Out[69]:
Aquí estamos violando un invariante: queremos que el número N
siempre sea igual al número de partículas en la lista: yo creo un nuevo constructor (que utiliza el constructor que ya había):
In [71]:
function Gas(particulas::Vector{Particula})
Gas(length(particulas), particulas)
end
Out[71]:
In [72]:
methods(Gas)
Out[72]:
In [73]:
g2 = Gas([Particula(1,2,3), Particula(4, 5, 6)])
Out[73]:
Si quiero hacer una lista vacía de partículas e irla ampliando:
In [74]:
particulas = Particula[]
Out[74]:
In [75]:
l = []
Out[75]:
NB: En Julia 0.4, l=[]
hace un arreglo de Any
, ya no de None
.
In [76]:
[3,4]
Out[76]:
In [83]:
v = Int[3,4]
Out[83]:
In [84]:
push!(v, 7)
Out[84]:
In [85]:
push!(v, "David")
In [78]:
Float64[3,4]
Out[78]:
In [79]:
String[3,4]
In [80]:
w = Any[3,4]
Out[80]:
In [82]:
push!(w, "David")
Out[82]:
In [86]:
particulas = Particula[]
Out[86]:
In [87]:
push!(particulas, Particula(1, 2, 3))
Out[87]:
In [88]:
methods(mover!)
In [89]:
function mover!(g::Gas)
# algo
end
Out[89]:
Haz un gas ideal:
x
y v
que son vectores con 2 entradas)Gas
(cuidar que el número de partículas se actualice)Objetos que representan pares $(u(x_0), u'(x_0))$ para una función $u: \mathbb{R} \to \mathbb{R}$
In [116]:
workspace()
In [117]:
type ValorDeriv
val
der
end
In [118]:
a = ValorDeriv(1, 2)
b = ValorDeriv(3, 4)
Out[118]:
In [119]:
a + b
In [120]:
+(a::ValorDeriv, b::ValorDeriv) = ValorDeriv(a.val+b.val, a.der+b.der)
Out[120]:
In [121]:
a + b
Out[121]:
a
y b
representan funciones $a(x)$ y $b(x)$ en algún lugar $x_0$ (implícito -- no viene representado explícitamente): a
representa
$\bar{a} = (a(x_0), a'(x_0))$
a + b
representa $((a+b)(x_0), (a+b)'(x_0))$
In [122]:
*(z::Number, a::ValorDeriv) = ValorDeriv(z*a.val, z*a.der)
Out[122]:
In [123]:
*(a::ValorDeriv, b::ValorDeriv) =
ValorDeriv(a.val*b.val, a.val*b.der + a.der*b.val)
Out[123]:
El lugar en el cual evalúo las funciones queda codificado en x
:
In [124]:
x = ValorDeriv(2, 1) # el lugar es el x_0=2
Out[124]:
In [125]:
p(x) = x*x + 2*x
Out[125]:
In [126]:
p(1)
Out[126]:
In [105]:
p(1.5)
Out[105]:
In [108]:
p(x)
Out[108]:
Este resultado contiene $(p(x_0), p'(x_0))$ --es decir, ¡¡se calculó automáticamente la derivada!!
In [127]:
import Base.sin
In [135]:
sin(a::ValorDeriv) = ValorDeriv(sin(a.val), cos(a.val)*a.der)
Out[135]:
In [129]:
f(x) = sin(x)
Out[129]:
In [130]:
f(x)
Out[130]:
In [137]:
g(x) = sin(x*x + 3x)
Out[137]:
In [138]:
g(x)
Out[138]:
In [133]:
sin(2*2)
Out[133]:
In [134]:
cos(2*2) * 2*2
Out[134]:
In [139]:
h(x) = sin(sin(x*x + 3x))
Out[139]:
In [140]:
h(x)
Out[140]:
In [142]:
import Base.show
In [145]:
show(io::IO, a::ValorDeriv) = print(io::IO, "Función con valor $(a.val) y
derivada $(a.der)")
Out[145]:
In [146]:
h(x)
Out[146]:
In [147]:
show(io::IO, a::ValorDeriv) = print(io::IO, "[$(a.val), $(a.der)]")
Out[147]:
In [148]:
h(x)
Out[148]:
In [150]:
z = h(x)
@sprintf "%.5f" z.val
Out[150]:
In [151]:
show(io::IO, a::ValorDeriv) = print(io::IO, "[$(@sprintf "%.5f" a.val), $(a.der)]")
Out[151]:
In [152]:
h(x)
Out[152]:
In [ ]:
"0.1"